package com.cy.pj.sys.service.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 通过切面学习Spring AOP中各种通知方法的应用
 */
//@Order(Ordered.HIGHEST_PRECEDENCE)//最高优先级
@Order(1)
@Aspect
@Component
public class SysTime {
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredTime)")
    public void doTime(){}

    /**
     * @Before描述的方法在目标业务执行之前执行
     *通常汇总@Before方法中做一些目标方法参数数据过滤或预处理
     * 所有非@Around通知方法中都可以使用JoinPoint作为连接点参数
     */
    @Before("doTime()")
    public void doBefore(JoinPoint joinPoint){//before after 只能使用 joinpoint
        Object[] args = joinPoint.getArgs();//可以对数据进行预处理
        System.out.println(Arrays.toString(args));
//        在这里拿到参数以后，可以对参数进行校验，校验不合法抛出异常
        System.out.println("@Before");
    }

    /**
     * @After描述的方法在目标执行之后执行（无论是否目标方法出现异常）
     *通过会在@After描述的方法中进行一些资源释放操作
     */
    @After("doTime()")
    public void doAfter(){
        System.out.println("@After");
    }

    /**
     * @AfterReturning描述的方法，在目标正常结束时执行
     * 例如，可以在这样的方法进行缓存更新操作（清除-cache.clear()、更新）
     */
    @AfterReturning("doTime()")
    public void doAfterReturning(){
        System.out.println("@AfterReturning");
    }

    /**
     * @AfterThrowing描述的方法，在目标方法异常结束时执行
     * 例如，在此方法中，可以进行错误日志的记录，可以报警（发送短信、邮件、启动启动器播放警报）
     */
    @AfterThrowing("doTime()")
    public void doAfterThrowing(){
        System.out.println("@AfterThrowing");
    }

    /**
     *   @Around描述的方法可以在内部通过连接点对象，手动调用目标方法（优先级最高）
     *   有Around的通知一般不用些其它通知了
     * @param joinPoint 连接点对象，@Around描述的方法只能应用ProceedingJoinPoint
     * @return 目标业务执行的结果
     * @throws Throwable
     */
    @Around("doTime()")
    public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
        System.out.println("@Around.before");
        try {
            //proceed的执行逻辑有可能执行本类的before，其它切面的before，目标方法（@Before、@AfterReturning， @AfterThrowing、 @After），
            // 当前或其它切面的后续通知方法，（再到这里后面的方法）
            Object result = joinPoint.proceed();
            System.out.println("@Around.afterReturning");
            return result;
        }catch (Throwable e){
            e.printStackTrace();
            System.out.println("@Around.AfterThrowing");
            throw e;
        }finally {
            System.out.println("@Around.After");
        }

    }
}
